//
//  KDefferedBlockQueue.m
//  MengLiao
//
//  Created by xiuxin on 2022/7/15.
//  Copyright © 2022 ZHBen. All rights reserved.
//

#import "KDefferedBlockQueue.h"

@interface KDefferedBlockOperation : NSBlockOperation

@property (nonatomic) BOOL kReady;

@property (nonatomic) BOOL forceMainThread;

@end

@implementation KDefferedBlockOperation

- (void)setKReady:(BOOL)kReady
{
    [self willChangeValueForKey:@"isReady"];
    _kReady = kReady;
    [self didChangeValueForKey:@"isReady"];
}

- (BOOL)isReady
{
    return [super isReady] && self.kReady;
}

- (void)main
{
    if (!self.forceMainThread || [[NSThread currentThread] isMainThread])
    {
        [super main];
    }
    else
    {
        [self performSelectorOnMainThread:@selector(main) withObject:nil waitUntilDone:YES];
    }
}

@end

@interface KDefferedBlockQueue()

@property (strong,nonatomic) NSDate * invokeTime;

@property (nonatomic,getter=isWaiting) BOOL waiting;

@property (strong,nonatomic) NSTimer * waitNextTimer;

@property (strong,nonatomic) NSTimer * waitIntervalTimer;

@end

@implementation KDefferedBlockQueue

- (instancetype)init
{
    if (self = [super init])
    {
        _free = YES;
        self.maxConcurrentOperationCount = 1;
        self.qualityOfService = NSQualityOfServiceUserInteractive;
    }
    return self;
}

- (void)addBlock:(dispatch_block_t)block
{
    _free = NO;
    KDefferedBlockOperation * operation = [KDefferedBlockOperation blockOperationWithBlock:block];
    operation.forceMainThread = self.forceMainThread;
    @weakify(self)
    operation.completionBlock = ^{
        @strongify(self)
        [self checkComplete];
    };
    [self addOperation:operation];
    if (self.invokeTime == nil || -[self.invokeTime timeIntervalSinceNow] > self.minimumInterval)
    {
        self.invokeTime = nil;
        [self waitNext];
    }
    else
    {
        [self waitInterval];
    }
}

- (void)waitInterval
{
    [self.waitIntervalTimer invalidate];
    NSTimeInterval interval = MAX(self.waitNextDuration, self.minimumInterval - [self.invokeTime timeIntervalSinceNow]);
    self.waitIntervalTimer = [NSTimer timerWithTimeInterval:interval target:self selector:@selector(waitIntervalOver) userInfo:nil repeats:NO];
    [[NSRunLoop mainRunLoop] addTimer:self.waitIntervalTimer forMode:NSRunLoopCommonModes];
}

- (void)waitIntervalOver
{
    self.waitIntervalTimer = nil;
    [self flush];
}

- (void)waitNext
{
    if (self.waitNextDuration > 0)
    {
        [self.waitNextTimer invalidate];
        self.waitNextTimer = [NSTimer timerWithTimeInterval:self.waitNextDuration target:self selector:@selector(waitNextOver) userInfo:nil repeats:NO];
        [[NSRunLoop mainRunLoop] addTimer:self.waitNextTimer forMode:NSRunLoopCommonModes];
    }
    else
    {
        [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(waitNextOver) object:nil];
        [self performSelectorOnMainThread:@selector(waitNextOver) withObject:nil waitUntilDone:NO modes:@[NSRunLoopCommonModes]];
    }
}

- (void)waitNextOver
{
    self.waitNextTimer = nil;
    [self flush];
}

- (void)flush
{
    for (KDefferedBlockOperation * operation in self.operations)
    {
        if (!operation.kReady)
        {
            operation.kReady = YES;
        }
    }
    self.invokeTime = [NSDate date];
}

- (BOOL)isTimeValid
{
    return !self.isWaiting && (self.invokeTime == nil || -[self.invokeTime timeIntervalSinceNow] > self.minimumInterval);
}

- (void)checkComplete
{
    for (NSOperation * operation in self.operations)
    {
        if (!operation.isFinished)
        {
            return;
        }
    }
    _free = YES;
    KExecuteBlock(self.completion);
}

@end


